//===--- Scope.cpp --------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/pil/gen/Scope.h"
#include "polarphp/basic/Range.h"

using namespace polar;
using namespace lowering;

ManagedValue Scope::popPreservingValue(ManagedValue mv) {
   // If we have a value, make sure that it is an object. The reason why is
   // that we want to make sure that we are not forwarding a cleanup for a
   // stack location that will be destroyed by this scope.
   assert(mv && mv.getType().isObject() &&
          (mv.getType().isTrivial(cleanups.SGF.F) ||
           mv.getOwnershipKind() == ValueOwnershipKind::None ||
           mv.hasCleanup()));
   CleanupCloner cloner(cleanups.SGF, mv);
   PILValue value = mv.forward(cleanups.SGF);
   pop();
   return cloner.clone(value);
}

// Since we have an RValue, we know that RValue invariants imply that all
// subvalues that are addresses must be address only types. Such address only
// types if they are +1 rvalues should be independent of any values from outside
// the +1 rvalue emission (that is if someone else has a reference to the
// address only type, we should have produced a copy). This means that it is
// safe to move the value into new memory that is guaranteed to live through the
// scope being pushed. As an additional complication due to PIL enforcing stack
// ordering, we can not use a temporary stack location since any stack locations
// that are inside the scope will be cleaned up while such a scope jumping stack
// is still alive (violating stack ordering). Instead we use an alloc_box to
// store the new value. allocbox-to-stack will then reorder/expand the stack
// lifetimes to resolve the issues.
static void lifetimeExtendAddressOnlyRValueSubValues(
   PILGenFunction &SGF, PILLocation loc,
   llvm::SmallVectorImpl<PILValue> &values,
   llvm::SmallVectorImpl<PILValue> &lifetimeExtendingBoxes) {
   for (PILValue &v : values) {
      // If v is not an address, it isn't interesting, continue.
      if (!v->getType().isAddress()) {
         continue;
      }

      // Otherwise, create the box and move the address only value into the box.
      assert(v->getType().isAddressOnly(SGF.F) &&
             "RValue invariants imply that all RValue subtypes that are "
             "addresses must be address only.");
      auto boxTy = PILBoxType::get(v->getType().getAstType());
      PILValue box = SGF.B.createAllocBox(loc, boxTy);
      PILValue addr = SGF.B.createProjectBox(loc, box, 0);
      SGF.B.createCopyAddr(loc, v, addr, IsTake, IsInitialization);

      // Then save the box so we create the box destroy in the caller and
      // overwrite v with the project box since that is where the value is now.
      lifetimeExtendingBoxes.emplace_back(box);
      v = addr;
   }
}

RValue Scope::popPreservingValue(RValue &&rv) {
   auto &SGF = cleanups.SGF;
   assert(rv.isPlusOne(SGF) && "Can only push plus one rvalues through a scope");

   // Perform a quick check if we have an incontext value. If so, just pop and
   // return rv.
   if (rv.isInContext()) {
      pop();
      return std::move(rv);
   }

   // After this point, we should have /no/ special states.
   assert(!rv.isInSpecialState());

   // Ok, we have a normal RValue. Gather all of the data that we need to
   // recreate the RValue in the outer scope.
   CanType type = rv.type;
   unsigned numEltsRemaining = rv.elementsToBeAdded;
   CleanupCloner cloner(SGF, rv);
   llvm::SmallVector<PILValue, 4> values;
   std::move(rv).forwardAll(SGF, values);

   // Lifetime any address only values that we may have.
   llvm::SmallVector<PILValue, 4> lifetimeExtendingBoxes;
   lifetimeExtendAddressOnlyRValueSubValues(SGF, loc, values,
                                            lifetimeExtendingBoxes);

   // Then pop the cleanups.
   pop();

   // Then create cleanups for any lifetime extending boxes that we may have to
   // ensure that the boxes are cleaned up /after/ the value stored in the
   // box. We assume that our values will be destroyed via a destroy_addr or the
   // like /before/ the end of our box's lifetime, implying that the value inside
   // the box should be uninitialized when the box is destroyed, so it is
   // important that we use a dealloc_box.
   for (auto v : lifetimeExtendingBoxes) {
      SGF.enterDeallocBoxCleanup(v);
   }

   // Reconstruct the managed values from the underlying sil values in the outer
   // scope. Since the RValue wants a std::vector value, we use that instead.
   std::vector<ManagedValue> managedValues;
   std::transform(
      values.begin(), values.end(), std::back_inserter(managedValues),
      [&cloner](PILValue v) -> ManagedValue { return cloner.clone(v); });

   // And then assemble the managed values into a rvalue.
   return RValue(SGF, std::move(managedValues), type, numEltsRemaining);
}

void Scope::popImpl() {
   verify();
   cleanups.innermostScope = savedInnermostScope;
   cleanups.endScope(depth, loc);
   if (cleanups.innermostScope)
      cleanups.stack.checkIterator(cleanups.innermostScope->depth);
   cleanups.popTopDeadCleanups();
}

void Scope::verify() {
   assert(cleanups.innermostScope == this && "popping scopes out of order");
   assert(depth.isValid());
   cleanups.stack.checkIterator(depth);
}
